package com.example.ourradio;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import com.squareup.otto.Produce;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.v4.app.NotificationCompat;

public class MediaPlayerService extends Service
	implements OnPreparedListener, OnErrorListener, OnCompletionListener {
	
	private interface DataSource {}
	private class URLDataSource implements DataSource {
		public String mUrl;
		public URLDataSource(String url) {
			mUrl = url;
		}
	}
	private class BytesDataSource implements DataSource {
		public byte[] mBytes;
		public BytesDataSource(byte[] bytes) {
			mBytes = bytes;
		}
	}
	
	public final static String ACTION_CONSTRUCT = 
		"com.example.ourradio.ACTION_CONSTRUCT";
	public final static String ACTION_INIT = "com.example.ourradio.ACTION_INIT";
	public final static String ACTION_PAUSE = 
		"com.example.ourradio.ACTION_PAUSE";
	public final static String ACTION_PLAY = "com.example.ourradio.ACTION_PLAY";
	public final static String ACTION_STOP = "com.example.ourradio.ACTION_STOP";
	public final static String ACTION_ORDER = 
		"com.example.ourradio.ACTION_ORDER";
	public final static String ACTION_NOTIFICATION =
		"com.example.ourradio.ACTION_NOTIFICATION";
	
	public final static String EXTRA_AUDIO_URL = 
	    "com.example.ourradio.EXTRA_AUDIO_URL";
	public final static String EXTRA_AUDIO_BYTES = 
		   "com.example.ourradio.EXTRA_AUDIO_BYTES";
	public final static String EXTRA_AUDIO_ORDER = 
		"com.example.ourradio.EXTRA_AUDIO_ORDER";
								
	public final int NOTIFICATION_ID = 0;
	
	MediaPlayer mMediaPlayer = null;
    WifiLock mWifiLock = null;
    AudioStatus mAudioStatus = new AudioStatus();
    AudioFragment.Order mOrder = AudioFragment.Order.SEQUENCE; 
    
    NotificationManager mNotificationManager = null;
    
    DataSource mDataSource = null;
    
    public int onStartCommand(Intent intent, int flags, int startId) {

    	if (intent.getAction().equals(ACTION_CONSTRUCT)) {
    		if (mAudioStatus.mAudioState == AudioState.NULL) {
    			construct();
    		}	
    	} else if (intent.getAction().equals(ACTION_INIT)) {
    		if (mAudioStatus.mAudioState == AudioState.IDLE) {
    			init(intent);
    		} else {
    			if (mMediaPlayer != null) {
    				mMediaPlayer.reset();
        		}
    			init(intent);
    		}
    	} 
    	else if (intent.getAction().equals(ACTION_PLAY)) {
    		if (mAudioStatus.mAudioState == AudioState.INITIALIZED ||
    			mAudioStatus.mAudioState == AudioState.STOPPED) {
    			prepareAsync();
    		} else if (mAudioStatus.mAudioState == AudioState.PAUSED ||
    				mAudioStatus.mAudioState == AudioState.COMPLETED) {
    			start(mMediaPlayer);
    		}
        } else if (intent.getAction().equals(ACTION_PAUSE)) {
        	if (mAudioStatus.mAudioState == AudioState.STARTED) {
        		pause(mMediaPlayer);
        	}
        } else if (intent.getAction().equals(ACTION_STOP)) {
        	if (mAudioStatus.mAudioState == AudioState.STARTED ||
        		mAudioStatus.mAudioState == AudioState.PAUSED) {
        		stop(mMediaPlayer);
            }	
        } else if (intent.getAction().equals(ACTION_ORDER)) {
        	mOrder = (AudioFragment.Order)intent.getSerializableExtra(
        		EXTRA_AUDIO_ORDER
        	);
        	if (mMediaPlayer != null && 
        		mAudioStatus.mAudioState == AudioState.STARTED ||
        		mAudioStatus.mAudioState == AudioState.PAUSED ||
        		mAudioStatus.mAudioState == AudioState.COMPLETED) {
        		mMediaPlayer.setLooping(
        			mOrder == AudioFragment.Order.SINGLE
        		);
        	}
        }
    	
    	return START_STICKY;
    }

    @Override
    public void onDestroy() {
		release();
		super.onDestroy();
	}
	
	@Override
	public void onPrepared(MediaPlayer mp) {
		start(mp);
	}

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // The MediaPlayer has moved to the Error state, must be reset!
    	mp.reset();
    	mAudioStatus.mAudioState = AudioState.IDLE;
    	setDataSource();
    	return true;
    }
	
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onCompletion(MediaPlayer mp) {
		mAudioStatus.mAudioState = AudioState.COMPLETED;
		updateStatus();
	}

	private void init(Intent intent) {
		if (intent.hasExtra(EXTRA_AUDIO_URL)) {
			mDataSource = 
				new URLDataSource(intent.getStringExtra(EXTRA_AUDIO_URL));	
		} else if (intent.hasExtra(EXTRA_AUDIO_BYTES)) {
			mDataSource = new BytesDataSource(
				intent.getByteArrayExtra(EXTRA_AUDIO_BYTES)
			);
		}  
		setDataSource();
	}
	
	private void construct() {
    	// Acquire WIFI lock
    	mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    		        .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
    	mWifiLock.acquire();
    			
    	// Initialize Media Player
    	mMediaPlayer = new MediaPlayer();
    	mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    	mMediaPlayer.setWakeMode(
    		getApplicationContext(), 
    		PowerManager.PARTIAL_WAKE_LOCK
    	);
    	mMediaPlayer.setOnPreparedListener(this);
    	mMediaPlayer.setOnErrorListener(this);
    	mMediaPlayer.setOnCompletionListener(this);
    	
		mNotificationManager = (NotificationManager) getSystemService(
	    	Context.NOTIFICATION_SERVICE
	    );	
		BusManager.getInstance().register(this);

    	mAudioStatus.mAudioState = AudioState.IDLE;
    }
    
	private void setDataSource(byte[] mp3SoundByteArray) {
	    try {
	        // create temp file that will hold byte array
	        File tempMp3 = File.createTempFile(
	        	"tts",
	        	"mp3",
	        	getCacheDir()
	        );
	        tempMp3.deleteOnExit();
		    FileOutputStream fos = new FileOutputStream(tempMp3);
	        fos.write(mp3SoundByteArray);
	        fos.close();
	        
	        // Tried reusing instance of media player
	        // but that resulted in system crashes...  
	        // MediaPlayer mediaPlayer = new MediaPlayer();

	        // Tried passing path directly, but kept getting 
	        // "Prepare failed.: status=0x1"
	        // so using file descriptor instead
	        FileInputStream fis = new FileInputStream(tempMp3);
	        mMediaPlayer.setDataSource(fis.getFD());
	        fis.close();
	    } catch (IOException ex) {
	        ex.printStackTrace();
	    }
	}

    private void setDataSource() {
	    try {
	    	if (mDataSource != null) {
	    		if (mDataSource instanceof URLDataSource) {
	    			mMediaPlayer.setDataSource(
	    				((URLDataSource) mDataSource).mUrl
	    			);
	    		} else if (mDataSource instanceof BytesDataSource) {
	    			setDataSource(((BytesDataSource) mDataSource).mBytes);
	    		}
	    		mAudioStatus.mAudioState = AudioState.INITIALIZED;
	    	}	
        } catch (IllegalArgumentException e) {
        	e.printStackTrace();
        	release();
        } catch (SecurityException e) {
        	e.printStackTrace();
        	release();
        } catch (IllegalStateException e) {
        	e.printStackTrace();
        	release();
        } catch (IOException e) {
			e.printStackTrace();
			release();
	    } 
    }
    
    private void prepareAsync() {
    	try {
    		if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
    			mMediaPlayer.prepareAsync();
        	}
    	} catch (IllegalArgumentException e) {
        	e.printStackTrace();
        	release();
        } catch (SecurityException e) {
        	e.printStackTrace();
        	release();
        } catch (IllegalStateException e) {
        	e.printStackTrace();
        	release();
        }
    }

    private void release() {
		if (mMediaPlayer != null) {
			mMediaPlayer.release();
			mMediaPlayer = null;
		}
		if (mWifiLock != null) {
			mWifiLock.release();
			mWifiLock = null;
        }
		mAudioStatus.mAudioState = AudioState.NULL;
		if (mNotificationManager != null) {
			mNotificationManager.cancel(NOTIFICATION_ID);
		}
		BusManager.getInstance().unregister(this);
	}
    
	private void start(MediaPlayer mp) {
		if (mp != null && !mp.isPlaying()) {
	    	mp.start();
	    	mp.setLooping(mOrder == AudioFragment.Order.SINGLE);
	    	mAudioStatus.mAudioState = AudioState.STARTED;
	    	updateStatus();
	    }	
    }
	
	private void pause(MediaPlayer mp) {
		if (mp != null && mp.isPlaying()) {
			mp.pause();
			mAudioStatus.mAudioState = AudioState.PAUSED;
			updateStatus();
	    }	
    }
	
	private void stop(MediaPlayer mp) {
		if (mp != null) {
			mp.stop();
			mAudioStatus.mAudioState = AudioState.STOPPED;
			updateStatus();
		 }
	}	

	private void updateStatus() {
		broadcast();
		setNotificationBar();
	}
	
	private void broadcast() {
		BusManager.getInstance().post(mAudioStatus);
	}
	
	private void setNotificationBar() {
        // Update notification status bar
    	PendingIntent pi = PendingIntent.getActivity(
    		getApplicationContext(), 
    		0,
            new Intent(ACTION_NOTIFICATION),
            PendingIntent.FLAG_UPDATE_CURRENT
        );
    	NotificationCompat.Builder builder =
    		new NotificationCompat.Builder(this)
    		 .setSmallIcon(R.drawable.ic_action_star)
    		 .setContentTitle("Our radio")
    		 .setContentText(mAudioStatus.mAudioState.toNotificationText())
    		 .setContentIntent(pi);
        mNotificationManager.notify(NOTIFICATION_ID, builder.build());
    }
	
	/*
	 * Send the event to AudioActivity when it registers to the bus 
	 */
	@Produce 
	public AudioStatus produceBusEvent() {
		return mAudioStatus;
	}
}
